package assembly

import (
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"gitee.com/kinwyb/appTools/appTool"
	"gitee.com/kinwyb/appTools/common"
	"github.com/emicklei/go-restful"
	"github.com/smallnest/rpcx/share"
)

// ContextValue 附加信息
type ContextValue struct {
	Token      string `description:"token标记"`
	UserID     int64  `description:"用户ID"`
	UserName   string `description:"用户名称"`
	EmpID      string `description:"员工工号"`
	EmpName    string `description:"员工名称"`
	RoomCode   string `description:"工厂"`
	Tracing    bool   `description:"开启追踪"`
	ServerMode string `description:"服务模式"`
}

// 校验是否为空
func (v *ContextValue) isEmpty() bool {
	return v.Token == "" && v.UserID < 1 && v.UserName == ""
}

func (v *ContextValue) Marshal() []byte {
	data, _ := json.Marshal(v)
	if len(data) > 0 {
		return data
	}
	return nil
}

func (v *ContextValue) Unmarshal(data []byte) error {
	return json.Unmarshal(data, v)
}

type Context struct {
	appTool.AppContext
	Value *ContextValue
}

func (c Context) Child() Context {
	ret := c
	ret.AppContext = c.AppContext.Child()
	return ret
}

func (c Context) Follows() Context {
	ret := c
	c.AppContext = c.AppContext.Follows()
	return ret
}

// RpcxCtx 转换成rpcx要求的标准上下文
func (c Context) RpcxCtx() context.Context {
	if c.Context == nil {
		c.Context = context.Background()
	}
	meta := c.Context.Value(share.ReqMetaDataKey)
	if meta == nil {
		meta = map[string]string{}
	}
	m := meta.(map[string]string)
	if common.Tracing {
		textMap := map[string]string{}
		c.TracingSpan().InjectTextMap(textMap)
		data, _ := json.Marshal(textMap)
		m["tracing"] = string(data)
		m["tracingType"] = fmt.Sprintf("%d", c.TracingType)
	}
	if c.Value != nil && !c.Value.isEmpty() {
		data := c.Value.Marshal()
		m["value"] = base64.StdEncoding.EncodeToString(data)
	}
	ctx := context.WithValue(c.Context, share.ReqMetaDataKey, meta)
	ctx = share.NewContext(ctx)
	if c.Value != nil && c.Value.ServerMode != "" {
		if cv, ok := ctx.(*share.Context); ok {
			cv.SetValue("serverModeKey", c.Value.ServerMode)
		}
	}
	return ctx
}

// NewRpcxContext 新建rpcx上下文信息
func NewRpcxContext(tracingname string, ctx context.Context) Context {
	meta := ctx.Value(share.ReqMetaDataKey)
	if meta == nil {
		meta = map[string]string{}
	}
	m := meta.(map[string]string)
	tracingText := m["tracing"]
	var ret Context
	if tracingText == "" {
		ret = NewContext(tracingname)
	} else {
		ret = NewContextWithTracing(
			common.NewTracingSpanExtractTextMap(
				tracingname,
				tracingText,
				m["tracingType"] == common.TracingFollowsFromString))
	}
	if m["value"] != "" {
		data, _ := base64.StdEncoding.DecodeString(m["value"])
		if len(data) > 0 {
			_ = ret.Value.Unmarshal(data)
		}
	}
	return ret
}

func NewContext(tracingname string) Context {
	return Context{
		AppContext: appTool.NewContext(tracingname),
		Value:      &ContextValue{},
	}
}

func NewContextWithTracing(tracingSpan common.TracingSpan) Context {
	return Context{
		AppContext: appTool.NewContextWithTracing(tracingSpan),
		Value:      &ContextValue{},
	}
}

func HttpRequestContextParse(ctx appTool.AppContext, request *restful.Request) appTool.Context {
	return Context{
		AppContext: ctx,
		Value: &ContextValue{
			Token:      "",
			UserID:     0,
			UserName:   "",
			EmpID:      "",
			EmpName:    "",
			RoomCode:   "",
			Tracing:    false,
			ServerMode: "",
		},
	}
}
